class PerformanceMetrics {
    constructor({ callback, errorCallback, whiteScreenConfig = {} }) {
        this.callback = callback;
        this.errorCallback = errorCallback;
        this.errors = [];
        this.webPerformance = { FP: '', FCP: '', FMP: '', LCP: '', isWhiteScreen: '' };
        this.whiteScreenConfig = Object.assign({ delay: 3000, threshold: 0.7, samplePoints: 9 }, whiteScreenConfig);

        this.initPerformanceObservers();
        this.initErrorListeners();
        this.initWhiteScreenCheck();
    }

    /** ========== 性能指标收集 ========== */
    initPerformanceObservers() {
        if (!('PerformanceObserver' in window)) return;

        // Paint 相关 (FP / FCP)
        this.observePerformance('paint', (entry) => {
            if (entry.name === 'first-paint') this.webPerformance.FP = entry.startTime;
            if (entry.name === 'first-contentful-paint') this.webPerformance.FCP = entry.startTime;
        });

        // Largest Contentful Paint (LCP)
        this.observePerformance('largest-contentful-paint', (entry) => {
            this.webPerformance.LCP = entry.startTime;
        });

        // 其他性能指标可在这里扩展...
    }

    observePerformance(type, handler) {
        try {
            const observer = new PerformanceObserver((list) => {
                list.getEntries().forEach(handler);
            });
            observer.observe({ type, buffered: true });
        } catch (e) {
            console.warn(`${type} observer 不支持`, e);
        }
    }

    /** ========== 错误监听 ========== */
    initErrorListeners() {
        // 运行时 & 资源加载错误
        window.addEventListener('error', (event) => {
            const errorInfo = this.formatErrorEvent(event);
            this.recordError(errorInfo);
        }, true);

        // Promise 未捕获错误
        window.addEventListener('unhandledrejection', (event) => {
            this.recordError({
                type: 'unhandledrejection',
                message: event.reason?.message || String(event.reason),
                stack: event.reason?.stack || null
            });
        });

        // 网络请求错误 (fetch)
        const originalFetch = window.fetch;
        window.fetch = (...args) => {
            return originalFetch(...args).then((res) => {
                if (!res.ok) {
                    this.recordError({
                        type: 'fetch-error',
                        message: `HTTP ${res.status}`,
                        url: args[0]
                    });
                }
                return res;
            }).catch((err) => {
                this.recordError({
                    type: 'fetch-exception',
                    message: err.message,
                    stack: err.stack
                });
                throw err;
            });
        };
    }

    formatErrorEvent(event) {
        const { target, message, filename, lineno, colno, error } = event;
        if (target instanceof HTMLScriptElement) return { type: 'js-load-error', src: target.src };
        if (target instanceof HTMLImageElement) return { type: 'img-load-error', src: target.src };
        if (target instanceof HTMLLinkElement && target.rel === 'stylesheet') return { type: 'css-load-error', href: target.href };
        return { type: 'js-error', message, filename, lineno, colno, stack: error?.stack };
    }

    recordError(errorInfo) {
        const info = { ...errorInfo, time: Date.now() };
        this.errors.push(info);
        if (this.errorCallback) this.errorCallback(info);
    }

    /** ========== 白屏检测 ========== */
    initWhiteScreenCheck() {
        window.addEventListener('load', () => {
            setTimeout(() => {
                requestAnimationFrame(() => {
                    const isWhiteScreen = this.detectWhiteScreen();
                    if (isWhiteScreen) {
                        this.recordError({ type: 'white-screen', message: '检测到白屏' });
                    }
                    this.webPerformance.isWhiteScreen = isWhiteScreen;
                });
            }, this.whiteScreenConfig.delay);
        });
    }

    detectWhiteScreen() {
        if (!this._hasDOMLoaded()) return true;
        const points = this.generateSamplingPoints(window.innerWidth, window.innerHeight, this.whiteScreenConfig.samplePoints);
        let emptyCount = points.filter(([x, y]) => this.isPointEmpty(document.elementsFromPoint(x, y))).length;
        return emptyCount / points.length > this.whiteScreenConfig.threshold;
    }

    generateSamplingPoints(width, height, count) {
        const cols = Math.ceil(Math.sqrt(count));
        const rows = Math.ceil(count / cols);
        const points = [];
        const marginX = width * 0.1, marginY = height * 0.1;
        const stepX = (width - marginX * 2) / (cols - 1);
        const stepY = (height - marginY * 2) / (rows - 1);

        for (let i = 0; i < rows; i++) {
            for (let j = 0; j < cols; j++) {
                if (points.length >= count) break;
                points.push([Math.round(marginX + j * stepX), Math.round(marginY + i * stepY)]);
            }
        }
        return points;
    }

    isPointEmpty(elements) {
        const filtered = elements.filter(el => !['HTML', 'BODY'].includes(el.tagName));
        if (!filtered.length) return true;
        const topEl = filtered[0];
        const rect = topEl.getBoundingClientRect();
        if (rect.width === 0 || rect.height === 0) return true;
        const style = window.getComputedStyle(topEl);
        if (this.isTransparentOrWhite(style.backgroundColor) && !this.hasVisibleContent(topEl)) {
            return true;
        }
        return false;
    }

    isTransparentOrWhite(color) {
        if (!color || color.includes('transparent')) return true;
        const rgb = color.match(/^rgba?\((\d+),\s*(\d+),\s*(\d+)/);
        if (rgb) {
            const [r, g, b] = rgb.slice(1).map(Number);
            return r > 240 && g > 240 && b > 240;
        }
        return false;
    }

    hasVisibleContent(el) {
        if (el.textContent?.trim()) return true;
        if (el instanceof HTMLImageElement && el.naturalWidth) return true;
        if (el instanceof HTMLCanvasElement || el instanceof HTMLVideoElement) return true;
        if (el instanceof HTMLInputElement) return true;
        return false;
    }

    _hasDOMLoaded() {
        return document.readyState === 'complete' && document.body?.childNodes.length > 0;
    }
}
